package com.it.myh.semantic;

import java.text.Normalizer;

public class JaroWinklerSimilarity {
    private static final double JW_SCALING_FACTOR = 0.1;
    private static final double JW_BOOST_THRESHOLD = 0.7;
    private static final int JW_PREFIX_LENGTH = 4;

    public static Integer removeCommonPrefix(String str1, String str2) {
        int minLength = Math.min(str1.length(), str2.length());

        int i = 0;
        while (i < minLength && str1.charAt(i) == str2.charAt(i)) {
            i++;
        }
        if (str1.length() != i && str2.length() != i){
            return i;
        } else {
            return -1;
        }

    }

    //计算语义差异度
    public static double semanticDif(String str1, String str2){
        Integer subFlag = removeCommonPrefix(str1, str2);
        if (subFlag != -1){
            double same = calculateSimilarity(str1.substring(subFlag), str2.substring(subFlag));
            return 1.0 - same;
        }
        return 0.0;
    }

    public static double calculateSimilarity(String str1, String str2) {
        // 标准化字符串
        str1 = normalizeString(str1);
        str2 = normalizeString(str2);

        // 计算Jaro距离
        double jaroDistance = calculateJaroDistance(str1, str2);

        // 计算Jaro-Winkler相似度
        double jaroWinklerSimilarity = calculateJaroWinklerSimilarity(jaroDistance, str1, str2);

        return jaroWinklerSimilarity;
    }

    private static String normalizeString(String str) {
        // 使用NFD规范化字符串，将字符分解为基字符和组合字符
        str = Normalizer.normalize(str, Normalizer.Form.NFD);
        // 移除组合字符
        str = str.replaceAll("\\p{M}", "");
        // 转换为小写
        str = str.toLowerCase();
        return str;
    }

    private static double calculateJaroDistance(String str1, String str2) {
        int len1 = str1.length();
        int len2 = str2.length();
        int maxDistance = Math.max(len1, len2) / 2 - 1;

        int[] matches1 = new int[len1];
        int[] matches2 = new int[len2];

        int matchesCount = 0;
        for (int i = 0; i < len1; i++) {
            for (int j = Math.max(0, i - maxDistance); j < Math.min(len2, i + maxDistance + 1); j++) {
                if (str1.charAt(i) == str2.charAt(j) && matches2[j] == 0) {
                    matches1[i] = 1;
                    matches2[j] = 1;
                    matchesCount++;
                    break;
                }
            }
        }

        if (matchesCount == 0) {
            return 0.0;
        }

        int transpositions = 0;
        int k = 0;
        for (int i = 0; i < len1; i++) {
            if (matches1[i] == 1) {
                while (matches2[k] == 0) {
                    k++;
                }
                if (str1.charAt(i) != str2.charAt(k)) {
                    transpositions++;
                }
                k++;
            }
        }

        return (matchesCount / (double) len1 + matchesCount / (double) len2 + (matchesCount - transpositions) / (double) matchesCount) / 3.0;
    }

    private static double calculateJaroWinklerSimilarity(double jaroDistance, String str1, String str2) {
        if (jaroDistance < JW_BOOST_THRESHOLD) {
            return jaroDistance;
        }

        int commonPrefixLength = 0;
        for (int i = 0; i < Math.min(JW_PREFIX_LENGTH, Math.min(str1.length(), str2.length())); i++) {
            if (str1.charAt(i) == str2.charAt(i)) {
                commonPrefixLength++;
            } else {
                break;
            }
        }

        return jaroDistance + commonPrefixLength * JW_SCALING_FACTOR * (1 - jaroDistance);
    }



    public static void main(String[] args) {
        String str1 = "北京市海淀区荷清路";
        String str2 = "北京市海淀区清华南路";

        double similarity = calculateSimilarity(str1, str2);
        System.out.println("相似度: " + similarity);
    }
}
